package com.biqee.framework.cookie;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.biqee.framework.encrypt.Encrypter;

public class CookieManager {
	private static final Logger logger = Logger.getLogger(CookieManager.class);
	private HttpServletRequest request;
	private String cookieTempName;
	
	public CookieManager(HttpServletRequest request,
			String cookieTempName) {
		super();
		this.request = request;
		this.cookieTempName = cookieTempName;
	}

	public CookieManager(HttpServletRequest request) {
		this(request, "__default_temp_cookie__");
	}
	
	public String getValue(String cookieName, String key) {
		return getValue(cookieName, key, "");
	}

	public String getValue(String cookieName, String key, String defaultValue) {
		Map<String, String> valueMap = parseCookie(cookieName);
		String value = valueMap.get(key);
		return StringUtils.isEmpty(value) ? defaultValue : value;
	}

	public void setValue(String cookieName, String key, String value) {
		Map<String, String> valueMap = parseCookie(cookieName);
		if (!StringUtils.isEmpty(value)) {
			valueMap.put(key, value);
		} else {
			valueMap.remove(key);
		}
	}

	private String getCookieValue(String cookieName, String defaultValue) {
		Cookie[] cookies = getRequest().getCookies();
		if (cookies != null) {
			for (int i = 0; i < cookies.length; i++) {
				Cookie cookie = cookies[i];
				if (cookieName.equals(cookie.getName())) {
					return cookie.getValue();
				}
			}
		}

		return defaultValue;
	}

	private String getDecryptedCookieValue(String cookieName) {
		String cookieValue = getCookieValue(cookieName, null);
		if (cookieValue == null) {
			return null;
		}

		try {
			String decodedValue = URLDecoder.decode(cookieValue, "ISO-8859-1");
			return new String(new Encrypter().decrypt(decodedValue
					.getBytes("ISO-8859-1")), "UTF-8");
		} catch (UnsupportedEncodingException e) {
			logger.error("failed to decrypt cookie value: " + cookieValue, e);
			return null;
		}
	}

	@SuppressWarnings("unchecked")
	private Map<String, String> parseCookie(String cookieName) {
		Map<String, String> valueMap = (Map<String, String>) getRequest()
				.getAttribute(cookieName);
		if (valueMap != null) {
			return valueMap;
		}

		valueMap = new HashMap<String, String>();
		getRequest().setAttribute(cookieName, valueMap);

		String cookieValue = getDecryptedCookieValue(cookieName);
		if (StringUtils.isEmpty(cookieValue)) {
			return valueMap;
		}

		String[] kvPairs = cookieValue.split("&");
		for (int i = 0; i < kvPairs.length; i++) {
			if (!StringUtils.isEmpty(kvPairs[i])) {
				int offset = kvPairs[i].indexOf('=');
				if (offset > 0) {
					String key = kvPairs[i].substring(0, offset);
					String value = kvPairs[i].substring(offset + 1);
					if (!StringUtils.isEmpty(key)
							&& !StringUtils.isEmpty(value)) {
						try {
							valueMap
									.put(key, URLDecoder.decode(value, "UTF-8"));
						} catch (UnsupportedEncodingException ex) {
							logger.error(
									"failed to decode cookie value: " + value,
									ex);
						}
					}
				}
			}
		}

		return valueMap;
	}
	
	public void save(HttpServletResponse response, String cookieName) {
		save(response, cookieName, null);
	}
	
	public void save(HttpServletResponse response, String cookieName, String domain) {
		save(response, cookieName, domain, "/");
	}
	
	public void save(HttpServletResponse response, String cookieName, String domain, String path) {
		save(response, cookieName, domain, path, -1);
	}
	
	public void cleanAll(HttpServletResponse response, String cookieName) {
		save(response, cookieName, null, "/", 0);
	}

	public void save(HttpServletResponse response, String cookieName, String domain, String path, int age) {
		Map<String, String> valueMap = parseCookie(cookieName);
		StringBuffer sb = new StringBuffer();

		for (Entry<String, String> e : valueMap.entrySet()) {
			String key = (String) e.getKey();
			if (!StringUtils.isEmpty(key)) {
				String value = (String) e.getValue();
				if (!StringUtils.isEmpty(value)) {
					if (sb.length() > 0) {
						sb.append('&');
					}

					sb.append(key);
					sb.append('=');

					try {
						sb.append(URLEncoder.encode(value, "UTF-8"));
					} catch (UnsupportedEncodingException ex) {
						logger
								.error("failed to encode cookie value: "
										+ value, ex);
					}
				}
			}
		}

		addEncryptedCookie(response, cookieName, sb.toString(), domain, path, age);
	}

	private void addEncryptedCookie(HttpServletResponse response, String cookieName, String cookieValue, String domain, String path,
			int age) {
		String encryptedValue;
		try {
			encryptedValue = new String(new Encrypter().encrypt(cookieValue
					.getBytes("UTF-8")), "ISO-8859-1");

			encryptedValue = URLEncoder.encode(encryptedValue, "ISO-8859-1");
		} catch (UnsupportedEncodingException e) {
			logger.error("failed to encrypt cookie value: " + cookieValue, e);
			return;
		}

		Cookie cookie = new Cookie(cookieName, encryptedValue);
		cookie.setPath(path);
		cookie.setMaxAge(age);
		if (domain != null) {			
			cookie.setDomain(domain);
		}
		response.addCookie(cookie);
	}

	private HttpServletRequest getRequest() {
		return request;
	}

	public void setCookieTempName(String cookieTempName) {
		this.cookieTempName = cookieTempName;
	}
}

